#ifndef XLCOMM_NET_BUFFER_H_
#define XLCOMM_NET_BUFFER_H_

#include <stdint.h>

#include <algorithm>
#include <string>
#include <vector>

#include "xlcomm/base/logger.h"
#include "xlcomm/copyable.h"
#include "xlcomm/define.h"
#include "xlcomm/net/endian.h"

namespace xlcomm {
namespace net {

// Buffer是自适应的，能够自动增长但不缩减大小（客户代码可以手动Shrink）
// +-------------------+------------------+------------------+
// | prependable bytes |  readable bytes  |  writable bytes  |
// |                   |     (CONTENT)    |                  |
// +-------------------+------------------+------------------+
// |                   |                  |                  |
// 0      <=     reader_index_  <=   writer_index_   <=     size
class XLCOMM_API Buffer : copyable {
 public:
  static const size_t kCheapPrepend = 8;

  Buffer(size_t initialSize = 1024)
      : buffer_(kCheapPrepend + initialSize),
        reader_index_(kCheapPrepend),
        writer_index_(kCheapPrepend) {}
  ~Buffer() {}

  // 从文件描述符读取数据到Buffer。返回值为读取到的长度
  ssize_t ReadFromFd(int fd, int* saved_errno);

  // 追加数据到Buffer（如果占不下 将扩展空间）
  void Append(const char* data, size_t len) {
    EnsureWritableBytes(len);
    memcpy(WriterPtr(), data, len);
    HasWritten(len);
  }
  void Append(const char* str) { Append(str, strlen(str)); }
  void Append(const void* data, size_t len) {
    Append(static_cast<const char*>(data), len);
  }
  void Append(const std::string& str) { Append(str.data(), str.size()); }
  void AppendInt8(int8_t host) { Append(&host, sizeof(host)); }
  void AppendInt16ToNet(int16_t host) {
    int16_t net = HostToNetwork16(host);
    Append(&net, sizeof(net));
  }
  void AppendInt32ToNet(int32_t host) {
    int32_t net = HostToNetwork32(host);
    Append(&net, sizeof(net));
  }
  void AppendInt64ToNet(int64_t host) {
    int64_t net = HostToNetwork64(host);
    Append(&net, sizeof(net));
  }

  // 前置追加数据到Buffer（如果占不下 将断言失败）
  void Prepend(const void* data, size_t len) {
    XLCOMM_ASSERT(len <= PrependableBytes());
    reader_index_ -= len;
    memcpy(ReaderPtr(), data, len);
  }
  void PrependInt8(int8_t host) { Prepend(&host, sizeof(host)); }
  void PrependInt16ToNet(int16_t host) {
    int16_t net = HostToNetwork16(host);
    Prepend(&net, sizeof(net));
  }
  void PrependInt32ToNet(int32_t host) {
    int32_t net = HostToNetwork32(host);
    Prepend(&net, sizeof(net));
  }
  void PrependInt64ToNet(int64_t host) {
    int64_t net = HostToNetwork64(host);
    Prepend(&net, sizeof(net));
  }

  // 从Buffer读出数据（如果没这么多内容 将断言失败）
  std::string ReadAllAsString() { return ReadString(ReadableBytes()); }
  std::string ReadString(size_t len) {
    XLCOMM_ASSERT(len <= ReadableBytes());
    std::string str(ReaderPtr(), len);
    HasRead(len);
    return str;
  }
  int8_t ReadInt8() {
    int8_t n = PeekInt8();
    HasRead(sizeof(n));
    return n;
  }
  int16_t ReadInt16ToHost() {
    int16_t n = PeekInt16ToHost();
    HasRead(sizeof(n));
    return n;
  }
  int32_t ReadInt32ToHost() {
    int32_t n = PeekInt32ToHost();
    HasRead(sizeof(n));
    return n;
  }
  int64_t ReadInt64ToHost() {
    int64_t n = PeekInt64ToHost();
    HasRead(sizeof(n));
    return n;
  }

  // 从Buffer窥探下数据，不读出（如果没这么多内容 将断言失败）
  int8_t PeekInt8() {
    int8_t net;
    XLCOMM_ASSERT(sizeof(net) <= ReadableBytes());
    memcpy(&net, ReaderPtr(), sizeof(net));
    return net;
  }
  int16_t PeekInt16ToHost() {
    int16_t net;
    XLCOMM_ASSERT(sizeof(net) <= ReadableBytes());
    memcpy(&net, ReaderPtr(), sizeof(net));
    return NetworkToHost16(net);
  }
  int32_t PeekInt32ToHost() {
    int32_t net;
    XLCOMM_ASSERT(sizeof(net) <= ReadableBytes());
    memcpy(&net, ReaderPtr(), sizeof(net));
    return NetworkToHost32(net);
  }
  int64_t PeekInt64ToHost() {
    int64_t net;
    XLCOMM_ASSERT(sizeof(net) <= ReadableBytes());
    memcpy(&net, ReaderPtr(), sizeof(net));
    return NetworkToHost64(net);
  }

  // 从Buffer查找数据，并返回位置（找不到返回nullptr）
  const char* FindCrLf() {
    const char* pos = std::search(ReaderPtr(), WriterPtr(), kCrLf, kCrLf + 2);
    return pos == WriterPtr() ? nullptr : pos;
  }

  // 获取读指针的地址
  const char* ReaderPtr() const { return BeginPtr() + reader_index_; }
  char* ReaderPtr() { return BeginPtr() + reader_index_; }
  // 读了多少字节数据，移动读索引
  void HasRead(size_t len) {
    XLCOMM_ASSERT(len <= ReadableBytes());
    if (len < ReadableBytes()) {
      reader_index_ += len;
    } else {
      HasReadAll();
    }
  }
  // 读到哪个位置，移动读索引
  void HasReadTo(const char* pos) { HasRead(pos - ReaderPtr()); }
  // 读了剩余所有数据，移动读写索引
  void HasReadAll() {
    reader_index_ = kCheapPrepend;
    writer_index_ = kCheapPrepend;
  }

  // 收缩Buffer的储备容量
  void Shrink(size_t reserve) {
    Buffer other;
    other.EnsureWritableBytes(ReadableBytes() + reserve);
    other.Append(ReaderPtr(), ReadableBytes());
    Swap(other);
  }

  // 交换两个Buffer的内容、索引
  void Swap(Buffer& other) {
    buffer_.swap(other.buffer_);
    std::swap(reader_index_, other.reader_index_);
    std::swap(writer_index_, other.writer_index_);
  }

  // 前置空间能容纳多少字节
  size_t PrependableBytes() const { return reader_index_; }
  // 多少字节有效数据
  size_t ReadableBytes() const { return writer_index_ - reader_index_; }
  // 还能写多少字节
  size_t WritableBytes() const { return Size() - writer_index_; }
  // 获取Buffer的总容量
  size_t Size() const { return buffer_.size(); }

  size_t reader_index() const { return reader_index_; }
  size_t writer_index() const { return writer_index_; }

 private:
  static const char kCrLf[];

  // 获取buffer_起始地址
  const char* BeginPtr() const { return &*(buffer_.begin()); }
  char* BeginPtr() { return &*(buffer_.begin()); }
  // 获取写指针的地址
  char* WriterPtr() { return BeginPtr() + writer_index_; }

  // 确保可写空间足够
  void EnsureWritableBytes(size_t len) {
    if (len > WritableBytes()) {
      MakeSpace(len);
    }
  }

  // 申请空间
  void MakeSpace(size_t len) {
    if (PrependableBytes() + WritableBytes() < len + kCheapPrepend) {
      // 可优化: move readable data
      buffer_.resize(writer_index_ + len);
    } else {
      // move readable data to the front, make space inside buffer
      std::copy(ReaderPtr(), WriterPtr(), BeginPtr() + kCheapPrepend);
      size_t readable = ReadableBytes();
      reader_index_ = kCheapPrepend;
      writer_index_ = reader_index_ + readable;
    }
  }

  // 写了多少字节数据，移动写索引
  void HasWritten(size_t len) {
    XLCOMM_ASSERT(writer_index_ + len <= Size());
    writer_index_ += len;
  }

  std::vector<char> buffer_;
  size_t reader_index_;
  size_t writer_index_;
};

}  // namespace net
}  // namespace xlcomm

#endif  // XLCOMM_NET_BUFFER_H_